package com.layblog.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.layblog.entity.Post;
import com.layblog.es.search.common.LayPostIndexKey;
import com.layblog.es.search.common.mq.LayPostMqIndexMessage;
import com.layblog.es.search.model.LayPostDocument;
import com.layblog.es.search.repository.LayPostRepository;
import com.layblog.service.PostService;
import com.layblog.service.ElasticSearchService;
import com.layblog.vo.PostVO;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;


/**
 * 使用ElasticSearch有两种方法：
 * 1.spring jpa的repository
 * 2.spring的template
 *
 * @Author 林必昭
 * @Date 2021/9/7 11:32
 */
@Slf4j
@Service
public class ElasticSearchServiceImpl implements ElasticSearchService {

    @Autowired
    LayPostRepository layPostRepository;
    @Autowired
    ModelMapper modelMapper;
    @Autowired
    private PostService postService;
    @Autowired
    private ElasticSearchService searchService;

    /**
     * query搜索查询方法逻辑实现
     *
     * @param page
     * @param q
     * @return
     */
    @Override
    public IPage<LayPostDocument> query(Page page, String q) {
        // 分页信息 mybatis plus的page 转成 jpa的page
        Long current = page.getCurrent() - 1;
        Long size = page.getSize();
        Pageable pageable = PageRequest.of(current.intValue(), size.intValue());

        //多个字段匹配只要满足一个即可返回结果
        MultiMatchQueryBuilder queryBuilder = QueryBuilders.multiMatchQuery(q,
                LayPostIndexKey.POST_TITLE,
                LayPostIndexKey.POST_AUTHOR,
                LayPostIndexKey.POST_CATEGORY,
                LayPostIndexKey.POST_DESCRIPTION,
                LayPostIndexKey.POST_TAGS);
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(queryBuilder)
                .withPageable(pageable).build();

        org.springframework.data.domain.Page<LayPostDocument> documents = layPostRepository.search(searchQuery);
        IPage pageData = new Page(page.getCurrent(), page.getSize(), documents.getTotalElements());
        pageData.setRecords(documents.getContent());
        return pageData;
    }

    /**
     * 初始化es数据，initEsData的关键是，首先将数据查出来，再通过ModelMapper将PostVo映射成PostDocument
     *
     * @param dataList
     * @return
     */
    @Override
    public int initEsData(List<PostVO> dataList) {
        if (dataList == null || dataList.isEmpty()) {
            return 0;
        }

        List<LayPostDocument> docs = new ArrayList<>();

        for (PostVO vo : dataList) {
            //调用ModelMapper将对象模型PostVo隐式映射成PostDocument
            LayPostDocument layPostDocument = modelMapper.map(vo, LayPostDocument.class);
            docs.add(layPostDocument);
        }
        //批量保存
        layPostRepository.saveAll(docs);
        return docs.size();
    }

    @Override
    public void initMySQLData2Es() {
        int total = 0;

        Page page = new Page<>();
        int size = 1000;

        for (int i = 1; i < 1000; i++) {
            page.setCurrent(i);
            page.setSize(size);
            IPage<PostVO> pageData = postService.paging(page, null, null, null, null, "created");
            int num = searchService.initEsData(pageData.getRecords());

            total += num;
            //当查询的数据比每页的数量少时说明是最后一页，这个时候break结束
            if (num < size) {
                break;
            }
        }
        log.info("初始化ES索引库数据成功，共：{} 条数据", total);
    }

    @Override
    public void createIndex(LayPostMqIndexMessage message) {
        try {
            Long postId = message.getPostId();
            if (ObjectUtil.isNotNull(postId)) {
                PostVO postVO = postService.selectOne(new QueryWrapper<Post>().eq("p.id", postId));
                LayPostDocument layPostDocument = modelMapper.map(postVO, LayPostDocument.class);
                //保存到ES
                layPostRepository.save(layPostDocument);
                log.info("-------- ES数据新增成功! -------- {}", layPostDocument);
            }

        } catch (Exception e) {
            log.error("【发表帖子】触发同步更新ES数据异常，{}", e);
        }
    }

    @Override
    public void updateIndex(LayPostMqIndexMessage message) {
        try {
            Long postId = message.getPostId();
            if (ObjectUtil.isNotNull(postId)) {
                PostVO postVO = postService.selectOne(new QueryWrapper<Post>().eq("p.id", postId));
                LayPostDocument layPostDocument = modelMapper.map(postVO, LayPostDocument.class);
                //更新ES数据为最新
                layPostRepository.save(layPostDocument);
                log.info("-------- ES数据更新成功! -------- {}", layPostDocument);
            }

        } catch (Exception e) {
            log.error("【编辑帖子】触发同步更新ES数据异常，{}", e);
        }
    }

    @Override
    public void removeIndex(LayPostMqIndexMessage message) {
        try {
            Long postId = message.getPostId();
            layPostRepository.deleteById(postId);
            log.info("-------- ES数据移除成功! --------");

        } catch (Exception e) {
            log.error("【删除帖子】触发同步更新ES数据异常，{}", e);
        }
    }
}
